package com.atguigu.tingshu.user.service.impl;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.dto.UpdateUserDto;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.user.context.UpdateUserPaidRecordStrategyContext;
import com.atguigu.tingshu.user.mapper.UserPaidAlbumMapper;
import com.atguigu.tingshu.user.mapper.UserPaidTrackMapper;
import com.atguigu.tingshu.user.strategy.UpdateUserPaidRecordStrategy;
import com.atguigu.tingshu.vo.user.GetUserInfoVo;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import com.alibaba.cloud.commons.lang.StringUtils;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.KafkaService;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.user.mapper.UserInfoMapper;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

	@Autowired
	private UserInfoMapper userInfoMapper;
	@Autowired
	private WxMaService wxService;
	@Autowired
	private KafkaService kafkaService;
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private UserPaidAlbumMapper userPaidAlbumMapper;
	@Autowired
	private UserPaidTrackMapper userPaidTrackMapper;

	@Override
	public String wxLogin(String code) {
		// 1.通过第三方工具,发送 AppId AppSecret code 获取用户的 openid
		if (StringUtils.isBlank(code)) {
			return "empty jscode";
		}
		String openid = null;
		try {
			WxMaJscode2SessionResult session = this.wxService.getUserService().getSessionInfo(code);
			openid = session.getOpenid();
		} catch (WxErrorException e) {
			e.printStackTrace();
			throw new GuiguException(ResultCodeEnum.DATA_ERROR);
		}
		// 2.通过 openid 查询用户信息是否存在,如果不存在
		if (StringUtils.isEmpty(openid))
			throw new GuiguException(ResultCodeEnum.DATA_ERROR);
		UserInfo userInfo = userInfoMapper.selectOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getWxOpenId, openid));
		if (userInfo == null) {
			// 3.构造用户信息对象,将基础信息封装到 UserInfo 对象中
			userInfo = new UserInfo();
			userInfo.setWxOpenId(openid);
			userInfo.setNickname("硅谷听友" + System.currentTimeMillis());
			userInfo.setAvatarUrl("https://oss.aliyuncs.com/aliyun_id_photo_bucket/default_handsome.jpg");
			// 4.对象保存到user_info 表
			userInfoMapper.insert(userInfo);
			// 5.将用户的id发送至kafka 等待账户服务初始化用户的账户数据
			kafkaService.sendMsg(String.valueOf(userInfo.getId()));
		}
		// 6.如果存在,生成token,加上前缀,存至 redis 缓存中
		String token = UUID.randomUUID().toString().replaceAll("-", "");
		String redisTokenKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
		redisTemplate.opsForValue().set(redisTokenKey, JSON.toJSONString(userInfo), 100, TimeUnit.DAYS);
		// 7.返回token
		return token;
	}

	@Override
	public GetUserInfoVo getUserInfo() {
		Long userId = AuthContextHolder.getUserId();
		UserInfo userInfo = this.getById(userId);
		GetUserInfoVo getUserInfoVo = new GetUserInfoVo();
		BeanUtils.copyProperties(userInfo, getUserInfoVo);
		return getUserInfoVo;
	}

	@Override
	public void updateUser(UpdateUserDto updateUserDto) {
		UserInfo userInfo = this.getById(AuthContextHolder.getUserId());
		BeanUtils.copyProperties(updateUserDto, userInfo);
		userInfoMapper.updateById(userInfo);
	}

	@Override
	public Map<Long, Integer> userIsPaidTrack(Long albumId, List<Long> trackIdList) {
		// 获取用户Id
		Long userId = AuthContextHolder.getUserId();
		// 构建返回结果 Map
		Map<Long, Integer> userPaidResultMap = new HashMap<>();
		// 根据专辑ID和用户ID查询用户专辑购买表
		UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>()
				.eq(UserPaidAlbum::getAlbumId, albumId)
				.eq(UserPaidAlbum::getUserId, userId));
		// 如果用户购买了专辑,就使用所有的声音变成已购买
		if (Objects.nonNull(userPaidAlbum)) {
			trackIdList.forEach(trackId -> userPaidResultMap.put(trackId, 1));
		} else {
			// 用户没有购买专辑,根据条件查询用户购买的声音列表
			List<UserPaidTrack> userPaidTracks = userPaidTrackMapper.selectList(new LambdaQueryWrapper<UserPaidTrack>()
					.eq(UserPaidTrack::getAlbumId, albumId)
					.eq(UserPaidTrack::getUserId, userId)
					.in(UserPaidTrack::getTrackId, trackIdList));
			// 提取结果中的每个对象的 trackId 形成列表
			List<Long> userPaidTrackIds = userPaidTracks.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
			// 声音列表中有id在用户购买声音列表中就置为已购买
			trackIdList.forEach(trackId->{
				Integer isContains = userPaidTrackIds.contains(trackId) ? 1 : 0;
				userPaidResultMap.put(trackId,isContains);
			});
		}
		return userPaidResultMap;
	}

	@Override
	public List<Long> findUserPaidTrackList(Long albumId) {
		Long userId = AuthContextHolder.getUserId();
		List<UserPaidTrack> userPaidTracks = userPaidTrackMapper
				.selectList(new LambdaQueryWrapper<UserPaidTrack>().eq(UserPaidTrack::getUserId, userId).eq(UserPaidTrack::getAlbumId, albumId));
		return userPaidTracks.stream().map(UserPaidTrack::getId).collect(Collectors.toList());
	}

	@Override
	public Boolean isUserPaidAlbum(Long albumId) {
		Long userId = AuthContextHolder.getUserId();
		UserPaidAlbum userPaidAlbum = userPaidAlbumMapper.selectOne(new LambdaQueryWrapper<UserPaidAlbum>()
				.eq(UserPaidAlbum::getUserId, userId)
				.eq(UserPaidAlbum::getAlbumId, albumId));
		return Objects.nonNull(userPaidAlbum);
	}

	@Override
	public void updateUserPaidRecord(UserPaidRecordVo userPaidRecordVo) {
		// 策略模式根据类别分别去不同的方法
		UpdateUserPaidRecordStrategy updateUserPaidRecordStrategy = UpdateUserPaidRecordStrategyContext
				.getUpdateUserPaidRecordStrategy(userPaidRecordVo.getItemType());
		updateUserPaidRecordStrategy.updateUserPaidRecord(userPaidRecordVo);
	}

	@Override
	public void updateVipExpireHandler() {
		// 把过期时间小于当前时间的所有用户的 isVip 字段全部改成 0
		this.update(new LambdaUpdateWrapper<UserInfo>().lt(UserInfo::getVipExpireTime,new Date()).set(UserInfo::getIsVip,0));
	}


}
